<!DOCTYPE html>
<html lang="en">

<head>
    <title>WebGL 2 Samples - geo_vertex_format</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <script src="third-party/gl-matrix-min.js"></script>
</head>

<body>
    <div id="info">WebGL 2 Samples - geo_vertex_format</div>
    <div id="description"> This sample demonstrates the use of different vertex formats. gl.HALF_FLOAT for normals and textures attribute.

    <!-- WebGL 2 shaders -->
    <script id="vs" type="x-shader/x-vertex">
        #version 300 es
        #define POSITION_LOCATION 1
        #define TEXCOORD_LOCATION 2
        #define NORMAL_LOCATION 3
        #define COLOR_LOCATION 4

        precision highp float;
        precision highp int;

        uniform mat4 u_model;
        uniform mat4 u_modelInvTrans; // To properly transform normals to world space
        uniform mat4 u_viewProj;
        uniform vec3 u_lightPosition;

        layout(location = POSITION_LOCATION) in vec3 a_position;
        layout(location = TEXCOORD_LOCATION) in vec2 a_texCoord;
        layout(location = NORMAL_LOCATION) in vec3 a_normal;

        // Vertex shader outputs to fragment shader
        out vec2 v_texCoord;
        out vec3 v_normal;
        out vec3 v_lightDirection;

        void main()
        {
            // Compute position
            vec3 modelPosition = vec3(u_model * vec4(a_position, 1.0));
            v_lightDirection = vec3(u_viewProj * vec4(u_lightPosition - modelPosition, 1.0));
            gl_Position = u_viewProj * vec4(modelPosition, 1.0);

            v_normal = vec3(u_viewProj * u_modelInvTrans * vec4(a_normal, 0.0));

            // Pass through
            v_texCoord = a_texCoord;
        }
    </script>

    <script id="fs" type="x-shader/x-fragment">
        #version 300 es
        #define POSITION_LOCATION 1
        #define TEXCOORD_LOCATION 2
        #define NORMAL_LOCATION 3
        #define COLOR_LOCATION 4
        #define LIGHT_DIRECTION_LOCATION 5

        precision highp float;
        precision highp int;

        uniform sampler2D s_tex2D;
        uniform float u_ambient;

        in vec2 v_texCoord;
        in vec3 v_normal;
        in vec3 v_lightDirection;
        out vec4 color;

        void main()
        {
            color = texture(s_tex2D, v_texCoord);

            float lightIntensity = dot(normalize(v_normal), normalize(v_lightDirection));
            lightIntensity = lightIntensity, 0.0, 1.0 + u_ambient;

            color = color * lightIntensity;
        }

    </script>

    <script src="utility.js"></script>
    <script src="HalfFloatUtility.js"></script>
    <script>
    (function () {
        'use strict';

        var canvas = document.createElement('canvas');
        canvas.width = Math.min(window.innerWidth, window.innerHeight);
        canvas.height = canvas.width;
        document.body.appendChild(canvas);

        var gl = canvas.getContext( 'webgl2', { antialias: false } );
        var isWebGL2 = !!gl;
        if(!isWebGL2) {
            document.getElementById('info').innerHTML = 'WebGL 2 is not available.  See <a href="https://www.khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">How to get a WebGL 2 implementation</a>';
            return;
        }

        // -- Init program
        var program = createProgram(gl, getShaderSource('vs'), getShaderSource('fs'));
        var unifModel = gl.getUniformLocation(program, 'u_model');
        var unifModelInvTrans = gl.getUniformLocation(program, 'u_modelInvTrans');
        var unifViewProj = gl.getUniformLocation(program, 'u_viewProj');
        var unifLightPosition = gl.getUniformLocation(program, 'u_lightPosition');

        var unifTex2D = gl.getUniformLocation(program, 's_tex2D');
        var unifAmbient = gl.getUniformLocation(program, 'u_ambient');

        // -- Init geometries
        var positions = new Float32Array([
          // Front face
          -1.0, -1.0,  1.0,
           1.0, -1.0,  1.0,
           1.0,  1.0,  1.0,
          -1.0,  1.0,  1.0,

          // Back face
          -1.0, -1.0, -1.0,
          -1.0,  1.0, -1.0,
           1.0,  1.0, -1.0,
           1.0, -1.0, -1.0,

          // Top face
          -1.0,  1.0, -1.0,
          -1.0,  1.0,  1.0,
           1.0,  1.0,  1.0,
           1.0,  1.0, -1.0,

          // Bottom face
          -1.0, -1.0, -1.0,
           1.0, -1.0, -1.0,
           1.0, -1.0,  1.0,
          -1.0, -1.0,  1.0,

          // Right face
           1.0, -1.0, -1.0,
           1.0,  1.0, -1.0,
           1.0,  1.0,  1.0,
           1.0, -1.0,  1.0,

          // Left face
          -1.0, -1.0, -1.0,
          -1.0, -1.0,  1.0,
          -1.0,  1.0,  1.0,
          -1.0,  1.0, -1.0
        ]);
        var vertexPosBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        var normals = HalfFloat.Float16Array([
            // Front face
            0, 0, -1,
            0, 0, -1,
            0, 0, -1,
            0, 0, -1,

            // Back face
            0, 0, 1,
            0, 0, 1,
            0, 0, 1,
            0, 0, 1,

            // Top face
            0, 1, 0,
            0, 1, 0,
            0, 1, 0,
            0, 1, 0,

            // Bottom face
            0, -1, 0,
            0, -1, 0,
            0, -1, 0,
            0, -1, 0,

            // Right face
            -1, 0, 0,
            -1, 0, 0,
            -1, 0, 0,
            -1, 0, 0,

            // Left face
            1, 0, 0,
            1, 0, 0,
            1, 0, 0,
            1, 0, 0
        ]);
        var vertexNorBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexNorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        var texCoords = HalfFloat.Float16Array([
            // Front face
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0,

            // Back face
            1.0, 1.0,
            1.0, 0.0,
            0.0, 0.0,
            0.0, 1.0,

            // Top face
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0,

            // Bottom face
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0,

            // Right face
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0,

            // Left face
            0.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0
        ]);
        var vertexTexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        // Element buffer
        var indexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);

        var cubeVertexIndices = [
          0,  1,  2,      0,  2,  3,    // front
          4,  5,  6,      4,  6,  7,    // back
          8,  9,  10,     8,  10, 11,   // top
          12, 13, 14,     12, 14, 15,   // bottom
          16, 17, 18,     16, 18, 19,   // right
          20, 21, 22,     20, 22, 23    // left
        ];

        // Now send the element array to GL

        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);

        // -- Init VertexArray

        var POSITION_LOCATION = 1;
        var TEXCOORD_LOCATION = 2;
        var NORMAL_LOCATION = 3;

        var vertexArray = gl.createVertexArray();
        gl.bindVertexArray(vertexArray);

        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.enableVertexAttribArray(POSITION_LOCATION);
        gl.vertexAttribPointer(POSITION_LOCATION, 3, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        gl.bindBuffer(gl.ARRAY_BUFFER, vertexNorBuffer);
        gl.enableVertexAttribArray(NORMAL_LOCATION);
        gl.vertexAttribPointer(NORMAL_LOCATION, 3, gl.HALF_FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
        gl.enableVertexAttribArray(TEXCOORD_LOCATION);
        gl.vertexAttribPointer(TEXCOORD_LOCATION, 2, gl.HALF_FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);

        gl.bindVertexArray(null);

        // -- Init Texture

        var imageUrl = '../assets/img/Di-3d.png';
        var texture;
        loadImage(imageUrl, function(image) {
            // -- Init 2D Texture
            texture = gl.createTexture();
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

            // -- Allocate storage for the texture
            gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGB8, 512, 512);
            gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGB, gl.UNSIGNED_BYTE, image);

            requestAnimationFrame(render);
        });

        // -- Initialize render variables
        var orientation = [0.0, 0.0, 0.0];

        var modelMatrix = mat4.create([
            1.0, 0.0, 0.0, 0.0,
            0.0, 1.0, 0.0, 0.0,
            0.0, 0.0, 1.0, 0.0,
            0.0, 0.0, 0.0, 1.0
        ]);

        var viewMatrix = mat4.create();
        var translate = vec3.create();
        vec3.set(translate, 0, 0, -10);
        mat4.translate(viewMatrix, modelMatrix, translate);
        var perspectiveMatrix = mat4.create();
        mat4.perspective(perspectiveMatrix, 0.785, 1, 1, 1000);
        var viewProj = mat4.create();

        var modelInvTrans = mat4.create();
        mat4.transpose(modelInvTrans, modelMatrix);
        mat4.invert(modelInvTrans, modelInvTrans);

        var lightPosition = [0.0, 0.0, 5.0];

        function render() {
            // -- Render
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT);

            gl.enable(gl.DEPTH_TEST);
            gl.enable(gl.CULL_FACE);
            gl.cullFace(gl.BACK);

            orientation[0] = 0.0050; // yaw
            orientation[1] = 0.0030; // pitch
            orientation[2] = 0.0009; // roll

            mat4.rotateX(viewMatrix, viewMatrix, orientation[0] * Math.PI);
            mat4.rotateY(viewMatrix, viewMatrix, orientation[1] * Math.PI);
            mat4.rotateZ(viewMatrix, viewMatrix, orientation[2] * Math.PI);

            gl.bindVertexArray(vertexArray);
            gl.useProgram(program);

            // Set uniforms
            gl.uniformMatrix4fv(unifModel, false, modelMatrix);
            gl.uniformMatrix4fv(unifModelInvTrans, false, modelInvTrans);

            mat4.multiply(viewProj, perspectiveMatrix, viewMatrix);
            gl.uniformMatrix4fv(unifViewProj, false, viewProj);

            var lP = new Float32Array(lightPosition);
            gl.uniform3fv(unifLightPosition, lP);
            gl.uniform1i(unifTex2D, 0);

            var ambient = 0.1;
            gl.uniform1f(unifAmbient, ambient);

            // Bind texture
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, texture);

            // Draw
            gl.drawElementsInstanced(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0, 1);

            requestAnimationFrame(render);

            // If you have a long-running page, and need to delete WebGL resources, use:
            //
            // gl.deleteBuffer(vertexPosBuffer);
            // gl.deleteBuffer(vertexTexBuffer);
            // gl.deleteBuffer(indexBuffer);
            // gl.deleteTexture(texture);
            // gl.deleteProgram(program);
            // gl.deleteVertexArray(vertexArray);

        }
    })();
    </script>
    <div id="highlightedLines"  style="display: none">#L199-L274</div>
</body>

</html>
